//#include <stdint.h>
#include "parser_helper.h"
#include "DM.h"
#include <algorithm>
#include <cassert>
//#define LIB_DEBUG
using namespace std;
using namespace nsLibCell;

//----------------------------------------------------------------------------------------------------------//
LibCell_C::LibCell_C(LibParserCellInfo* CellInfo): _CellInfo(CellInfo),_output_pin(),_nNumFanin(0)
{
    for(uint32_t i=0;i<_CellInfo->pins.size();i++){
        if(_CellInfo->pins[i].isInput!=true){
            _output_pin = i;
        } else {
            _nNumFanin++;
        }
    }
}
//----------------------------------------------------------------------------------------------------------//
LibCell_C::~LibCell_C ()
{
    delete _CellInfo;
}
//----------------------------------------------------------------------------------------------------------//
string LibCell_C::getName(void)
{
    return _CellInfo->name;
}
//----------------------------------------------------------------------------------------------------------//
string LibCell_C::getFootprint(void)
{
    return _CellInfo->footprint;
}
//----------------------------------------------------------------------------------------------------------//
bool LibCell_C::getPins(vector<string>& pins, pindir_E dir)
{
    vector<LibParserPinInfo>::iterator it;
    for(it=_CellInfo->pins.begin();it!=_CellInfo->pins.end();it++){
        if((*it).isInput==true){
            if(dir == e_input)
                pins.push_back((*it).name);
        } else {
            if(dir == e_output)
                pins.push_back((*it).name);
        }
    }
    return true;
}
//----------------------------------------------------------------------------------------------------------//
bool LibCell_C:: getPinDir(string pin,pindir_E& pindir_E) 
{
    vector<LibParserPinInfo>::iterator it;
    for(it=_CellInfo->pins.begin();it!=_CellInfo->pins.end();it++){
        if((*it).name==pin){
            if ((*it).isInput==true)
                pindir_E = e_input;
            else
                pindir_E = e_output;

            return true;
        }
    }
    return false;
}
//----------------------------------------------------------------------------------------------------------//
double LibCell_C::getLeakagePWR(void)
{
    return _CellInfo->leakagePower;
}
//----------------------------------------------------------------------------------------------------------//
double LibCell_C::getArea(void)
{
    return _CellInfo->area;
}
//----------------------------------------------------------------------------------------------------------//
bool LibCell_C::is_seq(void)
{
    return _CellInfo->isSequential;
}
//----------------------------------------------------------------------------------------------------------//
bool LibCell_C::is_donttouch(void)
{
    return _CellInfo->dontTouch;
}
//----------------------------------------------------------------------------------------------------------//
double LibCell_C::getCap(string pinName)
{
    //cout<<"getCap "<<pinName<<"="<<_CellInfo->pins[_getID(pinName)].capacitance<<endl;
    return _CellInfo->pins[_getID(pinName)].capacitance;
}
//----------------------------------------------------------------------------------------------------------//
double LibCell_C::getMaxCap(string pinName)
{
    return _CellInfo->pins[_getID(pinName)].maxCapacitance;
}
//----------------------------------------------------------------------------------------------------------//
double LibCell_C::getMaxCap(void) 
{
    return  _CellInfo->pins[_output_pin].maxCapacitance;
}
//----------------------------------------------------------------------------------------------------------//
bool LibCell_C::is_input(string pinName)
{
    return _CellInfo->pins[_getID(pinName)].isInput;
}
//----------------------------------------------------------------------------------------------------------//
bool LibCell_C::is_clock(string pinName)
{
    return _CellInfo->pins[_getID(pinName)].isClock;
}
//----------------------------------------------------------------------------------------------------------//
double LibCell_C::getVal(string& fromPin,string& toPin,string& Sense,
                    const double& fromPinTran,const double& toPinLoad, LUTMode_E Mode)
{
    
    //i for load
    //j for transition
    uint32_t i,j;

    double tran_factor=0.0;
    double load_factor=0.0;
    // d11 d21
    // d12 d22
    double d11,d12,d21,d22;
    LibParserLUT* _LUT;

    if(Mode==e_fall_delay)
        _LUT = &(_CellInfo->timingArcs[getArcId(fromPin,toPin,Sense)].fallDelay);
    else if(Mode==e_rise_delay)
        _LUT = &(_CellInfo->timingArcs[getArcId(fromPin,toPin,Sense)].riseDelay);
    else if(Mode==e_fall_tran)
        _LUT = &(_CellInfo->timingArcs[getArcId(fromPin,toPin,Sense)].fallTransition);
    else
        _LUT = &(_CellInfo->timingArcs[getArcId(fromPin,toPin,Sense)].riseTransition);
    //
    vector<double>::size_type szj = _LUT->transitionIndices.size();

    for(j=0;j<szj-1;j++) {
        //cout<<"dbg:: "<<i<<" "<<fromPinTran<<" "<<_LUT->transitionIndices[i]<<endl;
        if(_LUT->transitionIndices[j+1]>fromPinTran &&
            _LUT->transitionIndices[j]<=fromPinTran) {
            tran_factor = (fromPinTran-_LUT->transitionIndices[j])/(_LUT->transitionIndices[j+1]-_LUT->transitionIndices[j]);
            break;
        }
    }

    //
    vector<double>::size_type szi = _LUT->loadIndices.size();
    for(i=0;i<szi-1;i++){
        if(_LUT->loadIndices[i+1]>toPinLoad &&
            _LUT->loadIndices[i]<=toPinLoad) {
            load_factor = (toPinLoad-_LUT->loadIndices[i])/(_LUT->loadIndices[i+1]-_LUT->loadIndices[i]);
            break;
        }
    }
    double val = 0.0;
    //printf("(i,j)=(%d,%d)\n",i,j);
    if ( (i<szi-1) && (j<szj-1) ) {
        d11 = _LUT->tableVals[i][j];
        d12 = _LUT->tableVals[i+1][j];
        d21 = _LUT->tableVals[i][j+1];
        d22 = _LUT->tableVals[i+1][j+1];

        val = (d11*(1-load_factor)*(1-tran_factor)+
            d12*(1-tran_factor)*load_factor+
            d21*(1-load_factor)*tran_factor+
            d22*load_factor*tran_factor);
    } else {
        if ((i==szi-1) && (j<szj-1)) {
            d11 = _LUT->tableVals[i][j];
            d21 = _LUT->tableVals[i][j+1];
            val = d11+(d21-d11)*tran_factor;
        } else if ((i<szi-1) && (j==szj-1)) {
            d11 = _LUT->tableVals[i][j];
            d12 = _LUT->tableVals[i+1][j];
            val = d11+(d12-d11)*load_factor;
        } else {
            val = _LUT->tableVals[i][j];
        }
    }
#ifdef LIB_DEBUG
    cout<<"transition "<<fromPinTran<<" index="<<j<<" ("<<_LUT->transitionIndices[j]<<
    ","<<_LUT->transitionIndices[j+1]<<")"<<endl;

    cout<<"load "<<toPinLoad<<" index="<<i<<" ("<<_LUT->loadIndices[i]<<
    ","<<_LUT->loadIndices[i+1]<<")"<<endl;

    cout<<"("<<d11<<","<<d21<<")"<<endl;
    cout<<"("<<d12<<","<<d22<<")"<<endl;
    cout<<val<<endl;
#endif
    return val;

}
//----------------------------------------------------------------------------------------------------------//
double LibCell_C::getVal(const uint32_t& arcId,const double& fromPinTran,
                        const double& toPinLoad,LUTMode_E Mode) 
{
    //i for load
    //j for transition
    uint32_t i,j;

    double tran_factor=0.0;
    double load_factor=0.0;
    // d11 d21
    // d12 d22
    double d11,d12,d21,d22;
    LibParserLUT* _LUT;

    if(Mode==e_fall_delay)
        _LUT = &(_CellInfo->timingArcs[arcId].fallDelay);
    else if(Mode==e_rise_delay)
        _LUT = &(_CellInfo->timingArcs[arcId].riseDelay);
    else if(Mode==e_fall_tran)
        _LUT = &(_CellInfo->timingArcs[arcId].fallTransition);
    else
        _LUT = &(_CellInfo->timingArcs[arcId].riseTransition);
    //
    vector<double>::size_type szj = _LUT->transitionIndices.size();

    for(j=0;j<szj-1;j++) {
        //cout<<"dbg:: "<<i<<" "<<fromPinTran<<" "<<_LUT->transitionIndices[i]<<endl;
        if(_LUT->transitionIndices[j+1]>fromPinTran &&
            _LUT->transitionIndices[j]<=fromPinTran) {
            tran_factor = (fromPinTran-_LUT->transitionIndices[j])/(_LUT->transitionIndices[j+1]-_LUT->transitionIndices[j]);
            break;
        }
    }

    //
    vector<double>::size_type szi = _LUT->loadIndices.size();
    for(i=0;i<szi-1;i++){
        if(_LUT->loadIndices[i+1]>toPinLoad &&
            _LUT->loadIndices[i]<=toPinLoad) {
            load_factor = (toPinLoad-_LUT->loadIndices[i])/(_LUT->loadIndices[i+1]-_LUT->loadIndices[i]);
            break;
        }
    }

    double val = 0.0;
    if ( (i<szi-1) && (j<szj-1) ) {
        d11 = _LUT->tableVals[i][j];
        d12 = _LUT->tableVals[i+1][j];
        d21 = _LUT->tableVals[i][j+1];
        d22 = _LUT->tableVals[i+1][j+1];

        val = (d11*(1-load_factor)*(1-tran_factor)+
            d12*(1-tran_factor)*load_factor+
            d21*(1-load_factor)*tran_factor+
            d22*load_factor*tran_factor);
    } else {
        if ((i==szi-1) && (j<szj-1)) {
            d11 = _LUT->tableVals[i][j];
            d21 = _LUT->tableVals[i][j+1];
            val = d11+(d21-d11)*tran_factor;
        } else if ((i<szi-1) && (j==szj-1)) {
            d11 = _LUT->tableVals[i][j];
            d12 = _LUT->tableVals[i+1][j];
            val = d11+(d12-d11)*load_factor;
        } else {
            val = _LUT->tableVals[i][j];
        }
    }

#ifdef LIB_DEBUG
    cout<<"transition "<<fromPinTran<<" index="<<j<<" ("<<_LUT->transitionIndices[j]<<
    ","<<_LUT->transitionIndices[j+1]<<")"<<endl;

    cout<<"load "<<toPinLoad<<" index="<<i<<" ("<<_LUT->loadIndices[i]<<
    ","<<_LUT->loadIndices[i+1]<<")"<<endl;

    cout<<"("<<d11<<","<<d21<<")"<<endl;
    cout<<"("<<d12<<","<<d22<<")"<<endl;
    cout<<val<<endl;
#endif
    return val;
}
//----------------------------------------------------------------------------------------------------------//
inline uint32_t LibCell_C::_getID(string pinName)
{
    vector<LibParserPinInfo>::size_type sz = _CellInfo->pins.size();

    for(uint32_t i=0;i<sz;i++){
        if(_CellInfo->pins[i].name == pinName){
            return i;
        }
    }
    //nothing match
    cout<<"can not find "<<pinName<<endl;
    assert(0);   
}
//----------------------------------------------------------------------------------------------------------//
uint32_t LibCell_C::getArcId(string fromPin, string toPin, string Sense) 
{
    vector<LibParserTimingInfo>::size_type sz = _CellInfo->timingArcs.size();
    
    for(uint32_t i=0;i<sz;i++){
        if((_CellInfo->timingArcs[i].fromPin == fromPin) &&
            (_CellInfo->timingArcs[i].toPin == toPin) &&
            (_CellInfo->timingArcs[i].timingSense == Sense)){
            
            return i;
        }
    }
    //nothing match
    cout<<"can not find "<<fromPin<<" "<<toPin<<" "<<Sense<<endl;
    assert(0);
}
//----------------------------------------------------------------------------------------------------------//
void LibCell_C::showAttr(void) 
{
    const char* TF[2] = {"false","true"};
    vector<string> vTmp;
    printf("LibCell:\n");
    printf("  Name = %s\n",getName().data());
    printf("  Footprint = %s\n",getFootprint().data());
    printf("  Leakage Power = %f\n",getLeakagePWR());
    printf("  Area = %f\n",getArea());
    printf("  Is Seq = %s\n",TF[(uint32_t)is_seq()]);
    printf("  Is Fixed = %s\n",TF[(uint32_t)is_donttouch()]);
    
    printf("  Input pins = ");
    getPins(vTmp,e_input);
    for (uint32_t i=0;i<vTmp.size();i++)
        printf("%s ",vTmp[i].data());

    printf("\n  Output pins = ");
    getPins(vTmp,e_output);
    for (uint32_t i=0;i<vTmp.size();i++)
        printf("%s ",vTmp[i].data());

    printf("\n  Timing Arcs =\n");
    for(uint32_t i=0;i<_CellInfo->timingArcs.size();i++){
        printf("      %s -> %s (%s)\n",_CellInfo->timingArcs[i].fromPin.data()
                                    ,_CellInfo->timingArcs[i].toPin.data()
                                    ,_CellInfo->timingArcs[i].timingSense.data());
    }

}
//----------------------------------------------------------------------------------------------------------//
const LibParserLUT& LibCell_C::getTimingLUT(string p_strFromPin,string p_strToPin,nsLibCell::LUTMode_E p_eMode) const
{
    //LibParserCellInfo* _CellInfo;
    for(unsigned int i=0;i<_CellInfo->timingArcs.size();++i){
        if(0==strcmp(_CellInfo->timingArcs[i].fromPin.c_str(),p_strFromPin.c_str()) 
            && 0==strcmp(_CellInfo->timingArcs[i].toPin.c_str(),p_strToPin.c_str())){
            if(p_eMode==nsLibCell::e_fall_delay){
                return _CellInfo->timingArcs[i].fallDelay;
            } else if(p_eMode==nsLibCell::e_rise_delay){
                return _CellInfo->timingArcs[i].riseDelay;
            } else if(p_eMode==nsLibCell::e_fall_tran){
                return _CellInfo->timingArcs[i].fallTransition;
            } else if(p_eMode==nsLibCell::e_rise_tran){
                return _CellInfo->timingArcs[i].riseTransition;
            } 
        }
    }
    assert(0);
}

